Skip to content

feat: lets block for bundles#2355

Merged
snakster merged 2 commits into
mainfrom
snk-bundle-let
May 21, 2026
Merged

feat: lets block for bundles#2355
snakster merged 2 commits into
mainfrom
snk-bundle-let

Conversation

@snakster
Copy link
Copy Markdown
Contributor

@snakster snakster commented May 19, 2026

What this PR does / why we need it:

This PR adds support for a lets block in bundle definitions. The are evaluated after bundle inputs, but before exports and bundle stacks.

The lets defined expressions are available as bundle.let.<name> outside of the lets block.

Note: The PR also contains some opportunistic fixes in evalBundleExports.

Which issue(s) this PR fixes:

Implements #2299

Does this PR introduce a user-facing change?

yes, see changelog

Note

Medium Risk
Adds a new evaluation phase for bundle configuration (lets) that affects how bundle inputs/scaffolding/exports/stacks are computed, so regressions could change bundle rendering and UI behavior. Also refactors preempt.Run scheduling/drain logic, which is concurrency-sensitive but covered by new deadlock-focused tests.

Overview
Bundles now support a lets block in define bundle, merged across files and exposed to expressions as bundle.let.<name>, evaluated after inputs but before exports and bundle stacks.

Bundle evaluation is updated to populate bundle.file.path.* on the bundle namespace and to call config.LoadBundleLets() from core bundle eval as well as the scaffold and UI change flows so lets are available when computing scaffolding name/path and other derived fields.

Separately, preempt.Run is refactored to track in-flight goroutines and drain/resume waiters without deadlocking when a goroutine issues additional Await calls after receiving ErrUnresolvable, with new tests covering these late-await scenarios.

Reviewed by Cursor Bugbot for commit bf75b08. Bugbot is set up for automated code reviews on this repo. Configure here.

@snakster snakster changed the title Snk bundle let feat: lets block for bundles May 19, 2026
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 19, 2026

Preview of macos-15/go tests in bf75b08

🔍 View Details on Terramate Cloud

.
commands/scaffold
commands/ui
config
hcl
preempt
test

@snakster snakster marked this pull request as ready for review May 19, 2026 15:43
@snakster snakster requested a review from a team as a code owner May 19, 2026 15:43
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 19, 2026

Preview of ubuntu/go tests in bf75b08

🔍 View Details on Terramate Cloud

.
commands/scaffold
commands/ui
config
hcl
preempt
test

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 19, 2026

## Package: benchmarks/changed
metric: time/op
ChangeDetection-4: old 1.40ms ± 9%: new 1.35ms ± 8%: delta: -3.49%
ChangeDetectionTFAndTG-4: old 1.24ms ± 9%: new 1.28ms ± 7%: delta: 3.36%
metric: alloc/op
ChangeDetection-4: old 356kB ± 0%: new 356kB ± 0%: delta: 0.00%
ChangeDetectionTFAndTG-4: old 247kB ± 0%: new 247kB ± 0%: delta: 0.00%
metric: allocs/op
ChangeDetection-4: old 2.40k ± 0%: new 2.40k ± 0%: delta: 0.00%
ChangeDetectionTFAndTG-4: old 1.67k ± 1%: new 1.67k ± 2%: delta: 0.00%

---

## Package: cloud
metric: time/op
CloudReadLines-4: old 577µs ±22%: new 582µs ±19%: delta: 0.00%
CloudReadLine-4: old 3.28ms ±11%: new 3.28ms ± 9%: delta: 0.00%
metric: alloc/op
CloudReadLines-4: old 3.12MB ± 0%: new 3.12MB ± 0%: delta: 0.00%
CloudReadLine-4: old 3.37MB ± 0%: new 3.37MB ± 0%: delta: -0.00%
metric: allocs/op
CloudReadLines-4: old 5.54k ± 0%: new 5.54k ± 0%: delta: 0.00%
CloudReadLine-4: old 60.0k ± 0%: new 60.0k ± 0%: delta: 0.00%

---

## Package: fs
metric: time/op
ListFiles-4: old 25.6µs ± 6%: new 25.9µs ± 8%: delta: 0.00%
metric: alloc/op
ListFiles-4: old 27.6kB ± 0%: new 27.6kB ± 0%: delta: 0.00%
metric: allocs/op
ListFiles-4: old 335 ± 0%: new 335 ± 0%: delta: 0.00%

---

## Package: generate
metric: time/op
Generate-4: old 695ms ± 6%: new 684ms ± 6%: delta: 0.00%
GenerateRegex-4: old 431ms ± 2%: new 422ms ± 8%: delta: -2.06%
metric: alloc/op
Generate-4: old 2.23GB ± 0%: new 2.23GB ± 0%: delta: 0.00%
GenerateRegex-4: old 922MB ± 0%: new 922MB ± 0%: delta: 0.00%
metric: allocs/op
Generate-4: old 25.7M ± 0%: new 25.7M ± 0%: delta: 0.00%
GenerateRegex-4: old 18.1M ± 0%: new 18.1M ± 0%: delta: 0.00%

---

## Package: hcl/ast
metric: time/op
TokensForExpressionComplex-4: old 741µs ± 2%: new 757µs ±16%: delta: 0.00%
TokensForExpressionPlainStringNoNewline-4: old 297ns ± 2%: new 296ns ± 4%: delta: 0.00%
TokensForExpressionStringWith100Newlines-4: old 14.2µs ±55%: new 9.1µs ± 9%: delta: -35.97%
TokensForExpressionObjectWith100KeysWithNumberValues-4: old 1.16ms ±10%: new 0.86ms ± 5%: delta: -25.84%
TokensForExpression-4: old 873µs ±12%: new 719µs ± 5%: delta: -17.74%
metric: alloc/op
TokensForExpressionComplex-4: old 393kB ± 0%: new 393kB ± 0%: delta: 0.00%
TokensForExpressionPlainStringNoNewline-4: old 512B ± 0%: new 512B ± 0%: delta: 0.00%
TokensForExpressionStringWith100Newlines-4: old 12.5kB ± 0%: new 12.5kB ± 0%: delta: 0.00%
TokensForExpressionObjectWith100KeysWithNumberValues-4: old 393kB ± 0%: new 393kB ± 0%: delta: -0.02%
TokensForExpression-4: old 393kB ± 0%: new 393kB ± 0%: delta: -0.02%
metric: allocs/op
TokensForExpressionComplex-4: old 4.75k ± 0%: new 4.75k ± 0%: delta: 0.00%
TokensForExpressionPlainStringNoNewline-4: old 20.0 ± 0%: new 20.0 ± 0%: delta: 0.00%
TokensForExpressionStringWith100Newlines-4: old 227 ± 0%: new 227 ± 0%: delta: 0.00%
TokensForExpressionObjectWith100KeysWithNumberValues-4: old 3.09k ± 0%: new 3.09k ± 0%: delta: -0.03%
TokensForExpression-4: old 4.75k ± 0%: new 4.75k ± 0%: delta: 0.00%

---

## Package: hcl/eval
metric: time/op
PartialEvalComplex-4: old 242µs ± 9%: new 274µs ±12%: delta: 13.39%
PartialEvalSmallString-4: old 1.72µs ±12%: new 1.77µs ± 6%: delta: 2.92%
PartialEvalHugeString-4: old 870µs ± 9%: new 889µs ± 4%: delta: 2.14%
PartialEvalHugeInterpolatedString-4: old 2.33ms ±19%: new 2.13ms ±12%: delta: -8.41%
PartialEvalObject-4: old 15.9µs ±14%: new 11.8µs ±11%: delta: -26.20%
metric: alloc/op
PartialEvalComplex-4: old 360kB ± 0%: new 360kB ± 0%: delta: 0.00%
PartialEvalSmallString-4: old 1.94kB ± 0%: new 1.94kB ± 0%: delta: 0.00%
PartialEvalHugeString-4: old 196kB ± 0%: new 196kB ± 0%: delta: 0.00%
PartialEvalHugeInterpolatedString-4: old 4.30MB ± 0%: new 4.30MB ± 0%: delta: 0.00%
PartialEvalObject-4: old 19.4kB ± 0%: new 19.4kB ± 0%: delta: 0.00%
metric: allocs/op
PartialEvalComplex-4: old 3.72k ± 0%: new 3.72k ± 0%: delta: 0.00%
PartialEvalSmallString-4: old 25.0 ± 0%: new 25.0 ± 0%: delta: 0.00%
PartialEvalHugeString-4: old 38.0 ± 0%: new 38.0 ± 0%: delta: 0.00%
PartialEvalHugeInterpolatedString-4: old 25.1k ± 0%: new 25.1k ± 0%: delta: 0.00%
PartialEvalObject-4: old 179 ± 0%: new 179 ± 0%: delta: 0.00%

---

## Package: ls
metric: time/op
FindDefinition/10_files-4: old 22.8µs ± 9%: new 22.2µs ±11%: delta: 0.00%
FindDefinition/50_files-4: old 33.1µs ±13%: new 31.0µs ±10%: delta: -6.30%
FindDefinition/100_files-4: old 49.1µs ± 7%: new 41.4µs ±21%: delta: -15.78%
FindDefinition/500_files-4: old 181µs ± 7%: new 150µs ±11%: delta: -16.94%
FindReferences/10_files_5_refs-4: old 164µs ±20%: new 123µs ± 9%: delta: -25.14%
FindReferences/50_files_10_refs-4: old 476µs ±18%: new 450µs ±13%: delta: 0.00%
FindReferences/100_files_20_refs-4: old 898µs ±17%: new 788µs ±21%: delta: -12.25%
FindReferences/500_files_50_refs-4: old 5.75ms ±12%: new 3.50ms ± 4%: delta: -39.16%
Rename/10_files_5_refs-4: old 187µs ± 8%: new 115µs ± 4%: delta: -38.23%
Rename/50_files_10_refs-4: old 673µs ± 8%: new 436µs ±16%: delta: -35.27%
Rename/100_files_20_refs-4: old 1.33ms ± 9%: new 0.90ms ±12%: delta: -32.23%
SearchWorkspace/flat_100_files-4: old 1.10ms ±10%: new 0.84ms ±13%: delta: -24.11%
SearchWorkspace/flat_500_files-4: old 5.76ms ± 7%: new 4.30ms ±13%: delta: -25.38%
SearchWorkspace/nested_100_files_5_deep-4: old 1.11ms ± 8%: new 0.86ms ±17%: delta: -22.85%
SearchWorkspace/nested_500_files_5_deep-4: old 5.93ms ± 9%: new 4.60ms ±21%: delta: -22.55%
ParseFile/small_file_10_lines-4: old 40.5µs ± 9%: new 31.7µs ±16%: delta: -21.87%
ParseFile/medium_file_50_lines-4: old 177µs ±17%: new 124µs ± 5%: delta: -30.00%
ParseFile/large_file_200_lines-4: old 713µs ±21%: new 522µs ±14%: delta: -26.84%
ParseFile/huge_file_1000_lines-4: old 3.35ms ±21%: new 2.71ms ±10%: delta: -19.11%
metric: alloc/op
FindDefinition/10_files-4: old 15.2kB ± 0%: new 15.2kB ± 0%: delta: 0.00%
FindDefinition/50_files-4: old 20.1kB ± 0%: new 20.1kB ± 0%: delta: 0.00%
FindDefinition/100_files-4: old 26.4kB ± 0%: new 26.4kB ± 0%: delta: 0.00%
FindDefinition/500_files-4: old 72.8kB ± 0%: new 72.8kB ± 0%: delta: 0.00%
FindReferences/10_files_5_refs-4: old 87.0kB ± 0%: new 87.0kB ± 0%: delta: -0.01%
FindReferences/50_files_10_refs-4: old 336kB ± 0%: new 336kB ± 0%: delta: 0.00%
FindReferences/100_files_20_refs-4: old 646kB ± 0%: new 646kB ± 0%: delta: -0.01%
FindReferences/500_files_50_refs-4: old 3.13MB ± 0%: new 3.13MB ± 0%: delta: -0.02%
Rename/10_files_5_refs-4: old 105kB ± 0%: new 105kB ± 0%: delta: -0.01%
Rename/50_files_10_refs-4: old 359kB ± 0%: new 359kB ± 0%: delta: 0.00%
Rename/100_files_20_refs-4: old 677kB ± 0%: new 677kB ± 0%: delta: -0.01%
SearchWorkspace/flat_100_files-4: old 628kB ± 0%: new 628kB ± 0%: delta: 0.00%
SearchWorkspace/flat_500_files-4: old 3.09MB ± 0%: new 3.09MB ± 0%: delta: -0.01%
SearchWorkspace/nested_100_files_5_deep-4: old 636kB ± 0%: new 637kB ± 0%: delta: 0.00%
SearchWorkspace/nested_500_files_5_deep-4: old 3.13MB ± 0%: new 3.13MB ± 0%: delta: -0.02%
ParseFile/small_file_10_lines-4: old 34.7kB ± 0%: new 34.7kB ± 0%: delta: 0.00%
ParseFile/medium_file_50_lines-4: old 151kB ± 0%: new 151kB ± 0%: delta: 0.00%
ParseFile/large_file_200_lines-4: old 733kB ± 0%: new 733kB ± 0%: delta: 0.00%
ParseFile/huge_file_1000_lines-4: old 4.06MB ± 0%: new 4.06MB ± 0%: delta: 0.00%
metric: allocs/op
FindDefinition/10_files-4: old 209 ± 0%: new 209 ± 0%: delta: 0.00%
FindDefinition/50_files-4: old 291 ± 0%: new 291 ± 0%: delta: 0.00%
FindDefinition/100_files-4: old 392 ± 0%: new 392 ± 0%: delta: 0.00%
FindDefinition/500_files-4: old 1.19k ± 0%: new 1.19k ± 0%: delta: 0.00%
FindReferences/10_files_5_refs-4: old 960 ± 0%: new 960 ± 0%: delta: 0.00%
FindReferences/50_files_10_refs-4: old 3.81k ± 0%: new 3.81k ± 0%: delta: 0.00%
FindReferences/100_files_20_refs-4: old 7.34k ± 0%: new 7.34k ± 0%: delta: 0.00%
FindReferences/500_files_50_refs-4: old 35.9k ± 0%: new 35.9k ± 0%: delta: 0.00%
Rename/10_files_5_refs-4: old 1.15k ± 0%: new 1.15k ± 0%: delta: 0.00%
Rename/50_files_10_refs-4: old 4.09k ± 0%: new 4.09k ± 0%: delta: 0.00%
Rename/100_files_20_refs-4: old 7.74k ± 0%: new 7.74k ± 0%: delta: 0.00%
SearchWorkspace/flat_100_files-4: old 7.13k ± 0%: new 7.13k ± 0%: delta: 0.00%
SearchWorkspace/flat_500_files-4: old 35.1k ± 0%: new 35.1k ± 0%: delta: 0.00%
SearchWorkspace/nested_100_files_5_deep-4: old 7.18k ± 0%: new 7.18k ± 0%: delta: 0.00%
SearchWorkspace/nested_500_files_5_deep-4: old 35.2k ± 0%: new 35.2k ± 0%: delta: -0.00%
ParseFile/small_file_10_lines-4: old 315 ± 0%: new 315 ± 0%: delta: 0.00%
ParseFile/medium_file_50_lines-4: old 1.39k ± 0%: new 1.39k ± 0%: delta: 0.00%
ParseFile/large_file_200_lines-4: old 5.46k ± 0%: new 5.46k ± 0%: delta: 0.00%
ParseFile/huge_file_1000_lines-4: old 26.2k ± 0%: new 26.2k ± 0%: delta: 0.00%

---

## Package: stdlib
metric: time/op
TmAllTrueLiteralList-4: old 357µs ± 6%: new 400µs ±21%: delta: 12.18%
TmAllTrueFuncall-4: old 10.8µs ±12%: new 10.3µs ± 9%: delta: -4.59%
TmAnyTrueLiteralList-4: old 3.06ms ± 8%: new 2.65ms ±13%: delta: -13.34%
TmAnyTrueFuncall-4: old 11.9µs ±10%: new 10.1µs ± 6%: delta: -14.42%
TmTernary-4: old 1.19µs ± 7%: new 1.34µs ±12%: delta: 11.98%
TmTryUnknownFunc-4: old 1.08µs ± 7%: new 1.18µs ±11%: delta: 8.85%
TmTryUnknownVariable-4: old 1.08µs ± 7%: new 1.24µs ±21%: delta: 14.34%
TmTryUnknownObjectKey-4: old 1.28µs ± 3%: new 1.18µs ±12%: delta: -8.07%
SlugifyLargeList-4: old 1.33ms ± 3%: new 1.24ms ± 6%: delta: -6.60%
metric: alloc/op
TmAllTrueLiteralList-4: old 320kB ± 0%: new 320kB ± 0%: delta: 0.00%
TmAllTrueFuncall-4: old 10.4kB ± 0%: new 10.4kB ± 0%: delta: 0.00%
TmAnyTrueLiteralList-4: old 2.09MB ± 0%: new 2.09MB ± 0%: delta: 0.00%
TmAnyTrueFuncall-4: old 10.5kB ± 0%: new 10.5kB ± 0%: delta: 0.00%
TmTernary-4: old 1.18kB ± 0%: new 1.18kB ± 0%: delta: 0.00%
TmTryUnknownFunc-4: old 784B ± 0%: new 784B ± 0%: delta: 0.00%
TmTryUnknownVariable-4: old 768B ± 0%: new 768B ± 0%: delta: 0.00%
TmTryUnknownObjectKey-4: old 952B ± 0%: new 952B ± 0%: delta: 0.00%
SlugifyLargeList-4: old 614kB ± 0%: new 614kB ± 0%: delta: 0.00%
metric: allocs/op
TmAllTrueLiteralList-4: old 5.93k ± 0%: new 5.93k ± 0%: delta: 0.00%
TmAllTrueFuncall-4: old 265 ± 0%: new 265 ± 0%: delta: 0.00%
TmAnyTrueLiteralList-4: old 59.6k ± 0%: new 59.6k ± 0%: delta: 0.00%
TmAnyTrueFuncall-4: old 267 ± 0%: new 267 ± 0%: delta: 0.00%
TmTernary-4: old 27.0 ± 0%: new 27.0 ± 0%: delta: 0.00%
TmTryUnknownFunc-4: old 21.0 ± 0%: new 21.0 ± 0%: delta: 0.00%
TmTryUnknownVariable-4: old 20.0 ± 0%: new 20.0 ± 0%: delta: 0.00%
TmTryUnknownObjectKey-4: old 23.0 ± 0%: new 23.0 ± 0%: delta: 0.00%
SlugifyLargeList-4: old 18.0k ± 0%: new 18.0k ± 0%: delta: 0.00%

---

## Package: tg
metric: time/op
ModuleDiscovery-4: old 43.6ms ± 8%: new 37.7ms ±10%: delta: -13.51%
metric: alloc/op
ModuleDiscovery-4: old 36.8MB ± 0%: new 36.8MB ± 0%: delta: -0.04%
metric: allocs/op
ModuleDiscovery-4: old 451k ± 0%: new 451k ± 0%: delta: -0.01%

---

@snakster snakster force-pushed the snk-bundle-let branch 2 times, most recently from 5ec41ae to 8f82036 Compare May 20, 2026 08:43
Comment thread config/bundle.go Outdated
Comment thread config/bundle.go
Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 2e82b33. Configure here.

Comment thread commands/ui/change.go
@snakster snakster merged commit dfa0096 into main May 21, 2026
17 checks passed
@snakster snakster deleted the snk-bundle-let branch May 21, 2026 14:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant